bookwiz.io / app / api / books / [id] / files / [fileId] / route.ts
route.ts
Raw
import { createClient } from '@supabase/supabase-js'
import { NextResponse } from 'next/server'
import type { UpdateFileSystemItemRequest } from '@/lib/types/database'

// Create server-side Supabase client with user session
function createServerSupabaseClient(request: Request) {
  // Get the authorization header from the request
  const authHeader = request.headers.get('authorization')
  
  return createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      auth: {
        autoRefreshToken: false,
        persistSession: false
      },
      global: {
        headers: authHeader ? {
          Authorization: authHeader
        } : {}
      }
    }
  )
}

// GET /api/books/[id]/files/[fileId] - Get a specific file
export async function GET(
  request: Request,
  { params }: { params: { id: string; fileId: string } }
) {
  try {
    const supabase = createServerSupabaseClient(request)
    const bookId = params.id
    const fileId = params.fileId

    // Get the file system item (RLS will handle access control)
    const { data: item, error } = await supabase
      .from('file_system_items')
      .select('*')
      .eq('id', fileId)
      .eq('book_id', bookId)
      .single()

    if (error || !item) {
      return NextResponse.json(
        { error: 'File not found' },
        { status: 404 }
      )
    }

    return NextResponse.json({ item })
  } catch (error) {
    console.error('Error in GET /api/books/[id]/files/[fileId]:', error)
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    )
  }
}

// PUT /api/books/[id]/files/[fileId] - Update a file or folder
export async function PUT(
  request: Request,
  { params }: { params: { id: string; fileId: string } }
) {
  try {
    const supabase = createServerSupabaseClient(request)
    const bookId = params.id
    const fileId = params.fileId
    const body: UpdateFileSystemItemRequest = await request.json()

    // Check if file exists (RLS will handle access control)
    const { data: existingItem, error: existingError } = await supabase
      .from('file_system_items')
      .select('*')
      .eq('id', fileId)
      .eq('book_id', bookId)
      .single()

    if (existingError || !existingItem) {
      return NextResponse.json(
        { error: 'File not found' },
        { status: 404 }
      )
    }

    // If moving to a new parent, validate parent exists and is a folder
    if (body.parent_id && body.parent_id !== existingItem.parent_id) {
      const { data: parent, error: parentError } = await supabase
        .from('file_system_items')
        .select('id, type')
        .eq('id', body.parent_id)
        .eq('book_id', bookId)
        .single()

      if (parentError || !parent) {
        return NextResponse.json(
          { error: 'Parent folder not found' },
          { status: 400 }
        )
      }

      if (parent.type !== 'folder') {
        return NextResponse.json(
          { error: 'Parent must be a folder' },
          { status: 400 }
        )
      }
    }

    // Prepare update data
    const updateData: any = {}
    
    if (body.name !== undefined) updateData.name = body.name
    if (body.content !== undefined) updateData.content = body.content
    if (body.expanded !== undefined) updateData.expanded = body.expanded
    if (body.sort_order !== undefined) updateData.sort_order = body.sort_order
    if (body.parent_id !== undefined) updateData.parent_id = body.parent_id

    // Update the item
    const { data: updatedItem, error } = await supabase
      .from('file_system_items')
      .update(updateData)
      .eq('id', fileId)
      .eq('book_id', bookId)
      .select()
      .single()

    if (error) {
      console.error('Error updating file system item:', error)
      return NextResponse.json(
        { error: 'Failed to update item' },
        { status: 500 }
      )
    }

    return NextResponse.json({
      item: updatedItem,
      message: 'Item updated successfully'
    })
  } catch (error) {
    console.error('Error in PUT /api/books/[id]/files/[fileId]:', error)
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    )
  }
}

// DELETE /api/books/[id]/files/[fileId] - Delete a file or folder
export async function DELETE(
  request: Request,
  { params }: { params: { id: string; fileId: string } }
) {
  try {
    const supabase = createServerSupabaseClient(request)
    const bookId = params.id
    const fileId = params.fileId

    // Check if item exists (RLS will handle access control)
    const { data: existingItem, error: existingError } = await supabase
      .from('file_system_items')
      .select('id, type, name')
      .eq('id', fileId)
      .eq('book_id', bookId)
      .single()

    if (existingError || !existingItem) {
      return NextResponse.json(
        { error: 'Item not found' },
        { status: 404 }
      )
    }

    // Delete the item - CASCADE DELETE will handle children automatically
    const { error: deleteError } = await supabase
      .from('file_system_items')
      .delete()
      .eq('id', fileId)
      .eq('book_id', bookId)

    if (deleteError) {
      console.error('Error deleting file system item:', deleteError)
      return NextResponse.json(
        { error: 'Failed to delete item' },
        { status: 500 }
      )
    }

    return NextResponse.json({
      message: `${existingItem.type === 'file' ? 'File' : 'Folder'} deleted successfully`
    })
  } catch (error) {
    console.error('Error in DELETE /api/books/[id]/files/[fileId]:', error)
    return NextResponse.json(
      { error: error instanceof Error ? error.message : 'Internal server error' },
      { status: 500 }
    )
  }
}